#include <texture.h>
#include <cmath>
#include <config.h>
#include <math_tool.h>
#include <stb_declare.h>

void Texture::createTexture(const std::string& path) {
    std::unique_ptr<unsigned char[]> image(stbi_load(path.c_str(), &mWidth, &mHeight, &mChannel, 4));
    assert(mWidth == mHeight);
    if (image == nullptr) {
        throw std::runtime_error("failed to open texture in path " + path);
    }
    mMipmap.insert({mWidth, std::move(image)});
    int mipmapWidth = mWidth / 2;
    while (mipmapWidth != 1) {
        unsigned char* resizedImage = new unsigned char[mipmapWidth * mipmapWidth * 4];
        stbir_resize_uint8(mMipmap[mWidth].get(), mWidth, mHeight, 0, 
                           resizedImage, mipmapWidth, mipmapWidth, 0, 4);
        mMipmap.insert({mipmapWidth, std::unique_ptr<unsigned char[]>(resizedImage)});
        mipmapWidth /= 2;
    }
}

Color4f Texture::simple(float u, float v, float level) const {
    if (config.textureConfig.method == TextureConfig::SimpleMethod::nearest) {
        return nearestSimple(u, v);
    }
    else {
        return bilinearSimple(u, v);
    }
    // else {
    //     return trilinearSimple(u, v, level);
    // }
}

Color4f Texture::nearestSimple(float u, float v) const {
    return getTexel((int)(u * mWidth), (int)(v * mHeight), mWidth);
}

Color4f Texture::bilinearSimple(float u, float v) const {
    return bilinearLevelSimple(u, v, mWidth);
}

Color4f Texture::trilinearSimple(float u, float v, float mipmapLevel) const {
    // 计算查找层数
    float t = mipmapLevel - (int)mipmapLevel;
    // 在本层和下一层采用双线性插值
    Color4f color1 = bilinearLevelSimple(u, v, (int)mipmapLevel);
    Color4f color2 = bilinearLevelSimple(u, v, (int)mipmapLevel - 1);
    // 在两层间插值
    return lerp(color1, color2, t);
}

Color4f Texture::bilinearLevelSimple(float u, float v, int mipmapSize) const {
    float x = u * mipmapSize;
    float y = v * mipmapSize;

    int xFloor = (int)x;
    int yFloor = (int)y;

    float xInterpol = x - xFloor;
    float yInterpol = y - yFloor;

    Color4f color[4];
    // 获取右下角的三个纹素的颜色
    // [x] [-] -> [0] [2]
    // [-] [-] -> [1] [3]
    color[0] = getTexel(xFloor,     yFloor    , mipmapSize);
    color[1] = getTexel(xFloor,     yFloor + 1, mipmapSize);
    color[2] = getTexel(xFloor + 1, yFloor    , mipmapSize);
    color[3] = getTexel(xFloor + 1, yFloor + 1, mipmapSize);

    // 沿着y轴插值
    // [0]  -> [yIerp1] [2] -> [yIerp2]
    // [1]              [3]
    Color4f colorYInterpol1 = lerp(color[1], color[0], yInterpol);
    Color4f colorYInterpol2 = lerp(color[2], color[3], yInterpol);
    // 沿着x轴插值
    // [yIerp1] [yIerp2] -> simple_color
    return lerp(colorYInterpol1, colorYInterpol2, xInterpol);
}

Color4f Texture::getTexel(int x, int y, int mipmapSize) const {
    if      (mWarp == Warp::REPEAT) {
        repeat(x, y, mipmapSize);
    }
    else if (mWarp == Warp::MIRROR_REPEAT) {
        mirrorRepeat(x, y, mipmapSize);
    }
    else {
        clampToEdge(x, y, mipmapSize);
    }

    const unsigned char* mipmapTexture = mMipmap.at(mipmapSize).get();
    
    Color4f texel;
    int index = 4 * (x + y * mipmapSize);

    texel.r = mipmapTexture[index    ];
    texel.g = mipmapTexture[index + 1];
    texel.b = mipmapTexture[index + 2];
    texel.a = mipmapTexture[index + 3];

    return texel;
}

// 重复
void Texture::repeat(int& x, int& y, int mipmapSize) const {
    if (x >= mipmapSize) {
        x %= mipmapSize;
    }
    else if (x < 0) {
        x %= mipmapSize;
        x += mipmapSize;
    }

    if (y >= mipmapSize) {
        y %= mipmapSize;           
    }
    else if (y < 0) {
        y %= mipmapSize;
        y += mipmapSize;
    }
}

// 镜像(没想好)
void Texture::mirrorRepeat(int& x, int& y, int mipmapSize) const {
    repeat(x, y, mipmapSize);
}

// 钳制
void Texture::clampToEdge(int& x, int& y, int mipmapSize) const {
    if (x >= mipmapSize) {
        x = mipmapSize - 1;
    }
    else if (x < 0) {
        x = 0;
    }

    if (y >= mipmapSize) {
        y = mipmapSize - 1;
    }
    else if (y < 0) {
        y = 0;
    }
}